#include "src/kernel/types.h"
#include "src/kernel/stat.h"
#include "src/user/user.h"

#include <stdarg.h>

static char digits[] = "0123456789ABCDEF";

/**
 * @brief 将字符c输出到文件描述符fd
 * @param fd 文件描述符
 * @param c 字符
*/
static void
putc(int fd, char c)
{
    write(fd, c, 1);
}

/**
 * @brief 向文件描述符fd输出整数xx
 * @param fd 文件描述符
 * @param xx 整数
 * @param base 进制数
 * @param sgn 是否输出符号
*/
static void
printint(int fd, int xx, int base, int sgn)
{   
    char buf[16];
    int i, neg;
    uint x;

    if (sgn && xx < 0) {
        neg = 1;
        x = -xx;
    } else {
        neg = 0;
        x = xx;
    }

    i = 0;
    do
    {
        buf[i++] = digits[x % base];
    } while ((x /= base) != 0);
    if (neg)
        buf[i++] = '-';
    
    while (i-- > 0)
        putc(fd, buf[i]);
}

/**
 * @brief 向文件描述符fd输出指针x
 * @param fd 文件描述符
 * @param x 指针
*/
static void 
printptr(int fd, uint64 x)
{
    int i;
    putc(fd, '0');
    putc(fd, 'x');
    for (i = 0; i < sizeof(uint64) * 2; i++, x <<= 4)
        putc(fd, digits[x >> (sizeof(uint64) * 8 - 4)]);
}

/**
 * @brief 向文件描述符fd输出标准格式字符串
 *        仅支持%d, %l, %x, %p, %s, %c, %%
 * @param fd 文件描述符
 * @param fmt 标准格式字符串
 * @param ap 可变参数列表
*/
void
vprintf(int fd, const char *fmt, va_list ap)
{
    char *s;
    int c, i, state;

    state = 0;
    for (i = 0; fmt[i]; i++) {
        c = fmt[i] & 0xff;
        if (state != '%') {
            if (c == '%')
                state = '%';
            else
                putc(fd, c);
        } else {
            if (c == 'd')
                printint(fd, va_arg(ap, int), 10, 1);
            else if (c == 'l')
                printint(fd, va_arg(ap, uint64), 10, 0);
            else if (c == 'x')
                printint(fd, va_arg(ap, int), 16, 0);
            else if (c == 'p')
                printptr(fd, va_arg(ap, uint64));
            else if (c == 's') {
                s = va_arg(ap, char*);
                if (s == 0)
                    s = "(null)";
                while(*s != 0)
                    putc(fd, *s++);
            } else if (c == 'c')
                putc(fd, va_arg(ap, uint));
            else if (c == '%')
                putc(fd, '%');
            else {
                putc(fd, '%');
                putc(fd, c);
            }
            state = 0;
        }
    }
}

void 
fprintf(int fd, const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    vprintf(fd, fmt, ap);
}

void
printf(const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    vprintf(1, fmt, ap);
}